﻿#include "cJSON.h"
#include "xml_parser.h"
#include "umessage.h"

void umsg_parse_json_add(unsigned short type, char *pbuf, umsg_cb cb)
{
	if (!pbuf)
		goto bail1;
	
	cJSON *pjson = cJSON_Parse(pbuf);
	if (!pjson)
		goto bail1;

	cJSON *jsnode = NULL;
	char *txt = NULL;
	
	jsnode = cJSON_GetObjectItem(pjson, "file");
	if (!jsnode || !jsnode->valuestring || !*jsnode->valuestring)
		goto bail2;

	txt = jsnode->valuestring;

	xmlDocPtr docptr = xml_get_docptr(txt);
	if (!docptr)
		goto bail2;
	
	jsnode = cJSON_GetObjectItem(pjson, "query");
	if (!jsnode || !jsnode->valuestring || !*jsnode->valuestring)
		goto bail2;
	
	const xmlChar *query =  (const xmlChar *)jsnode->valuestring;

    int insert = xinsert_append;
	jsnode = cJSON_GetObjectItem(pjson, "insert");
	if (jsnode)
    {
        insert = jsnode->valueint;
    }

	xmlNodePtr p = xml_add_node(docptr, query,(xml_insert_t)insert);
	if (!p)
		goto bail2;

	jsnode = cJSON_GetObjectItem(pjson, "node");
	if (jsnode)
	{
		cJSON *child_node = NULL;
        child_node = cJSON_GetObjectItem(jsnode, "name_str");
        if (child_node)
        {
		    xmlNodeSetName(p, BAD_CAST child_node->valuestring);
        }

        child_node = cJSON_GetObjectItem(jsnode, "value_str");
        if (child_node)
        {
		    xmlNodeSetContent(p, BAD_CAST child_node->valuestring);
        }
	}
		
	jsnode = cJSON_GetObjectItem(pjson, "prop");
	if (jsnode)
	{
		cJSON *child_arr = NULL;
        int i;
        
        int arr_size = cJSON_GetArraySize(jsnode);
        
        for (i = 0; i < arr_size; i++)
        {        
            child_arr = cJSON_GetArrayItem(jsnode, i);
            if (child_arr)
            {
                cJSON *prop_name = NULL;
                cJSON *prop_value = NULL;
                prop_name = cJSON_GetObjectItem(child_arr, "name_str");
                prop_value = cJSON_GetObjectItem(child_arr, "value_str");
                if (prop_value && prop_name)
        		    xmlSetProp(p, BAD_CAST prop_name->valuestring, BAD_CAST prop_value->valuestring);
            }
        }		
	}

    if (cb)
    {
        cb(type, txt);
    }
	
bail2:
	if (pjson)
		cJSON_Delete(pjson);
bail1:
	return;		
}

void umsg_parse_json_delete(unsigned short type, char *pbuf, umsg_cb cb)
{
    if (!pbuf)
		goto bail1;
	
	cJSON *pjson = cJSON_Parse(pbuf);
	if (!pjson)
		goto bail1;

	cJSON *jsnode = NULL;
	char *txt = NULL;
	
	jsnode = cJSON_GetObjectItem(pjson, "file");
	if (!jsnode || !jsnode->valuestring || !*jsnode->valuestring)
			goto bail2;

	txt = jsnode->valuestring;
    
	xmlDocPtr docptr = xml_get_docptr(txt);
	if (!docptr)
	{
		goto bail2;
	}
	
	jsnode = cJSON_GetObjectItem(pjson, "query");
	if (!jsnode || !jsnode->valuestring || !*jsnode->valuestring)
		goto bail2;
	
	const xmlChar *query =  (const xmlChar *)jsnode->valuestring;

	xml_del_node(docptr, query);

    if (cb)
    {
        cb(type, txt);
    }
	
bail2:
	if (pjson)
		cJSON_Delete(pjson);
bail1:
	return;
}

void umsg_parse_json_modify(unsigned short type, char *pbuf, umsg_cb cb)
{
	if (!pbuf)
		goto bail1;
	
	cJSON *pjson = cJSON_Parse(pbuf);
	if (!pjson)
		goto bail1;

	cJSON *jsnode = NULL;
	char *txt = NULL;
	
	jsnode = cJSON_GetObjectItem(pjson, "file");
	if (!jsnode || !jsnode->valuestring || !*jsnode->valuestring)
			goto bail2;

	txt = jsnode->valuestring;
    
	xmlDocPtr docptr = xml_get_docptr(txt);
	if (!docptr)
	{
		goto bail2;
	}
	
	jsnode = cJSON_GetObjectItem(pjson, "query");
	if (!jsnode || !jsnode->valuestring || !*jsnode->valuestring)
		goto bail2;
	
	const xmlChar *query =  (const xmlChar *)jsnode->valuestring;

	xmlXPathObjectPtr xpathObj = xml_get_query_result(docptr, query);
	if (!xpathObj)
		goto bail2;

	xmlNodeSetPtr nodeset;
	int i;

	nodeset = xpathObj->nodesetval;

	jsnode = cJSON_GetObjectItem(pjson, "node");
	if (NULL != jsnode)
	{	
		cJSON *child_node = NULL;
        char *name_str = NULL;
        char *value_str = NULL;
        child_node = cJSON_GetObjectItem(jsnode, "name_str");
        if (child_node)
        {
            name_str = child_node->valuestring;
        }

        child_node = cJSON_GetObjectItem(jsnode, "value_str");
        if (child_node)
        {
            value_str = child_node->valuestring;
        }

        if(!xmlXPathNodeSetIsEmpty(nodeset))
    	{
    		for (i = 0; i < nodeset->nodeNr; i++)
    		{
                if (name_str)
                    xmlNodeSetName(nodeset->nodeTab[i], BAD_CAST name_str);

                if (value_str)
                    xmlNodeSetContent(nodeset->nodeTab[i], BAD_CAST value_str);
    		}
    	}
	}
		
	jsnode = cJSON_GetObjectItem(pjson, "prop");
	if (NULL != jsnode)
	{
		cJSON *child_arr = NULL;
        int j;
        
        int arr_size = cJSON_GetArraySize(jsnode);

        if(!xmlXPathNodeSetIsEmpty(nodeset))
    	{
    		for (i = 0; i < nodeset->nodeNr; i++)
    		{        
                for (j = 0; j < arr_size; j++)
                {        
                    child_arr = cJSON_GetArrayItem(jsnode, j);
                    if (child_arr)
                    {
                        cJSON *prop_name = NULL;
                        cJSON *prop_value = NULL;
                        prop_name = cJSON_GetObjectItem(child_arr, "name_str");
                        prop_value = cJSON_GetObjectItem(child_arr, "value_str");
                        
                        if (prop_value && prop_name && prop_name->valuestring)
                		    xmlSetProp(nodeset->nodeTab[i], BAD_CAST prop_name->valuestring, BAD_CAST prop_value->valuestring);
                    }
                }
    		}
    	}
	}

	xml_release_query(xpathObj);

    if (cb)
    {
        cb(type, txt);
    }
	
bail2:
	if (pjson)
		cJSON_Delete(pjson);
bail1:
	return;
}

void umsg_parse_json_reload(unsigned short type, char *pbuf, umsg_cb cb)
{
    if (!pbuf)
        goto bail1;
    
    cJSON *pjson = cJSON_Parse(pbuf);
    if (!pjson)
        goto bail1;

    cJSON *jsnode = NULL;
    char *txt = NULL;
    
    jsnode = cJSON_GetObjectItem(pjson, "file");
    if (!jsnode || !jsnode->valuestring || !*jsnode->valuestring)
        goto bail2;

    txt = jsnode->valuestring;

    xml_reset_docptr(txt);
    
    if (cb)
    {
        cb(type, txt);
    }
	
bail2:
	if (pjson)
		cJSON_Delete(pjson);
bail1:
	return;    
}

void umsg_parse_json_stopsaving(unsigned short type, char *pbuf, umsg_cb cb)
{
    if (!pbuf)
        goto bail1;
    
    cJSON *pjson = cJSON_Parse(pbuf);
    if (!pjson)
        goto bail1;

    cJSON *jsnode = NULL;
    char *txt = NULL;
    
    jsnode = cJSON_GetObjectItem(pjson, "file");
    if (!jsnode || !jsnode->valuestring || !*jsnode->valuestring)
        goto bail2;

    txt = jsnode->valuestring;

    xml_clean_docptr(txt);
    
    if (cb)
    {
        cb(type, txt);
    }
	
bail2:
	if (pjson)
		cJSON_Delete(pjson);
bail1:
	return;    
}

void umsg_parse_json_handle2(unsigned short type, char *pbuf, umsg_cb cb)
{
	switch(type)
	{
    	case MSG_DATA_ADD:
    		umsg_parse_json_add(type, pbuf, cb);
    		break;

        case MSG_DATA_DEL:
    		umsg_parse_json_delete(type, pbuf, cb);
    		break;
        
        case MSG_DATA_MOD:
    		umsg_parse_json_modify(type, pbuf, cb);
    		break;

        case MSG_DATA_STOPSAVING:
            umsg_parse_json_stopsaving(type, pbuf, cb);
            break;

        case MSG_DATA_RELOAD:
            umsg_parse_json_reload(type, pbuf, cb);
            break;
	}
}

void umsg_parse_json_handle(unsigned short type, char *pbuf)
{
    umsg_parse_json_handle2(type, pbuf, NULL);
}

static void umsg_pack_json_by_valuetype(cJSON *json, va_list *app)
{
    msg_jstype_value_t value_t = va_arg(*app, msg_jstype_t);

    switch(value_t)
    {
    case msg_jstype_value_number:
    {
        char *name = va_arg(*app, char *);
        double value = va_arg(*app, double);
        cJSON_AddNumberToObject(json, name, value);    
        break;
    }

    case msg_jstype_value_string:
    {
        char *name = va_arg(*app, char *);
        char *value = va_arg(*app, char *);
        cJSON_AddStringToObject(json, name, value);  
        break;
    }

    default:
        break;
    };
}

int umsg_pack_json (char **ppbuf, ...)
{    
    cJSON *json;
    va_list ap;
	msg_jstype_t xcurr;

    va_start(ap, ppbuf);

    json = cJSON_CreateObject();  

    while((xcurr = va_arg(ap, msg_jstype_t)) != msg_jstype_null) {
        switch(xcurr) {
        case msg_jstype_node: 
		{
            cJSON *child_node = cJSON_CreateObject();
            cJSON_AddItemToObject(json, "node", child_node); 
			msg_jstype_node_t xnodecurr;
			while((xnodecurr = va_arg(ap, msg_jstype_node_t)) != msg_jstype_node_null)
			{
				switch (xnodecurr)
				{
				case msg_jstype_node_name:
                case msg_jstype_node_value:
				{
					umsg_pack_json_by_valuetype(child_node, &ap);
					break;
				}

				default:
				{
		            goto bail;
		        }	
				}
			}
			
			break;
        }
		
        case msg_jstype_prop: 
		{
            cJSON *arr_prop = cJSON_CreateArray();
            cJSON_AddItemToObject(json, "prop", arr_prop); 
            
			msg_jstype_prop_t xpropcurr;
			while((xpropcurr = va_arg(ap, msg_jstype_prop_t)) != msg_jstype_prop_null)
			{
                switch (xpropcurr)
                {
                case msg_jstype_prop_pair:
                {
                	cJSON *child_prop = cJSON_CreateObject();
                    cJSON_AddItemToArray(arr_prop, child_prop); 
                    umsg_pack_json_by_valuetype(child_prop, &ap);
                    umsg_pack_json_by_valuetype(child_prop, &ap);
                    break;
                }

                default:
                    goto bail;
                }
			}
			
			break;
        }

        case msg_jstype_normal:
        {
            umsg_pack_json_by_valuetype(json, &ap);
            break;
        }

		default: 
		{
            goto bail;
        }
        }
    }

    va_end(ap);

    *ppbuf = cJSON_Print(json);
    if (!*ppbuf)
        goto bail;
    
    cJSON_Delete(json);
    
    cJSON_Minify(*ppbuf);

    return strlen(*ppbuf);

bail:
    va_end(ap);
    
    cJSON_Delete(json);

    return -1;
}

char *umsg_pack_msg(char *pbuf, unsigned short len, unsigned char dev, unsigned short type)
{
    if (!pbuf)
        return NULL;

    pbuf = (char *)realloc(pbuf, len + sizeof(Start_st) + sizeof(unsigned char));

    memmove(pbuf + sizeof(Start_st), pbuf, len);

    Start_st *pstart = (Start_st *)pbuf;

    pstart->startcode = USOCK_START_CODE;
    pstart->ss_dev = dev;
    pstart->ss_type = type;
    pstart->ss_len = len;

    *(pbuf + len + sizeof(Start_st)) = USOCK_END_CODE;

    return pbuf;
}

int main()
{
    char *pbuf = NULL;

    umsg_pack_json(&pbuf,
                    UMSG_SET_JSON_NORMAL(UMSG_SET_JSON_STRING("file","a.xml")),
                    UMSG_SET_JSON_NORMAL(UMSG_SET_JSON_STRING("query","/root_record/record")),
                    UMSG_SET_JSON_NORMAL(UMSG_SET_JSON_NUMBER("insert",1)),
                    UMSG_SET_JSON_NODE(
                        UMSG_SET_JSON_NODE_VALUE(UMSG_SET_JSON_STRING("value_str","1234")),
                        UMSG_SET_JSON_NODE_NULL()),
                    UMSG_SET_JSON_PROP(
                        UMSG_SET_JSON_PROP_PAIR(UMSG_SET_JSON_STRING("name_str","1234"),UMSG_SET_JSON_STRING("value_str","wer")),
                        UMSG_SET_JSON_PROP_NULL()),
                    UMSG_SET_JSON_NULL());
    return 0;
}
